import java.util.HashMap;

public class FuzzySetsManager {

  private HashMap<FuzzySet, MembershipFunction> membershipFunctions = 
      new HashMap<FuzzySet, MembershipFunction>(FuzzySet.values().length);
   
  private FuzzySetsManager() {
	  updateDesiredParameters(2, 20); // arbitrary, default d_desired, v_desired
  }
  
  private FuzzySetsManager(HashMap<FuzzySet, MembershipFunction> membershipFunctions) {
    this.membershipFunctions = membershipFunctions;
  }

  private static FuzzySetsManager manager = null;

  public static FuzzySetsManager getInstance() {
    if (manager == null) {
      manager = new FuzzySetsManager();
    } 
    return manager;
  }
  
  /*
   * Updates the d_desired and v_desired parameters of the membership functions
   */
  public void updateDesiredParameters(double d_desired, double v_desired) {
    membershipFunctions.put(FuzzySet.D_VERYCLOSE, 
        new TriangularLeftStepMembershipFunction(0, 0.5 * d_desired));
    membershipFunctions.put(FuzzySet.D_CLOSE, 
        new TriangularMembershipFunction(0.5 * d_desired, 0.5 * d_desired));
    membershipFunctions.put(FuzzySet.D_JUSTRIGHT, 
        new TriangularMembershipFunction(d_desired, 0.5 * d_desired));
    membershipFunctions.put(FuzzySet.D_FAR, 
        new TriangularMembershipFunction(1.5 * d_desired, 0.5 * d_desired));
    membershipFunctions.put(FuzzySet.D_VERYFAR, 
        new TriangularRightStepMembershipFunction(2 * d_desired, 0.5 * d_desired));
    membershipFunctions.put(FuzzySet.V_VERYSLOW, 
        new TriangularLeftStepMembershipFunction(v_desired - 8, 4));
    membershipFunctions.put(FuzzySet.V_SLOW, 
        new TriangularMembershipFunction(v_desired - 4, 4));
    membershipFunctions.put(FuzzySet.V_JUSTRIGHT, 
        new TriangularMembershipFunction(v_desired, 2));
    membershipFunctions.put(FuzzySet.V_FAST, 
        new TriangularMembershipFunction(v_desired + 4, 4));
    membershipFunctions.put(FuzzySet.V_VERYFAST, 
        new TriangularRightStepMembershipFunction(v_desired + 8, 4));
    membershipFunctions.put(FuzzySet.A_BRAKEHARD, 
        new TriangularMembershipFunction(-4, 0.5));
    membershipFunctions.put(FuzzySet.A_BRAKE, 
        new TriangularMembershipFunction(-1.5, 0.5));
    membershipFunctions.put(FuzzySet.A_NONE, 
        new TriangularMembershipFunction(0, 0.5));
    membershipFunctions.put(FuzzySet.A_ACCELERATE, 
        new TriangularMembershipFunction(1.5, 0.5));
    membershipFunctions.put(FuzzySet.A_ACCELERATEHARD, 
        new TriangularMembershipFunction(4, 0.5));
    // System.out.println("d_des: " + d_desired + "v_des: " + v_desired);
  }
  
  /*
   * Returns the degree of validity of a membership function, as determined by
   * intersecting the membership function with the fuzzy singleton x
   */
  public double getDegreeOfValidity(FuzzySet fuzzySet, double x) {
    return membershipFunctions.get(fuzzySet).getValue(x);
  }
  
  /*
   * Returns an array of values representing the qualified consequent of a 
   * membership function given a firing strength
   * Use getLowerBound() to get the domain element associated with the first value 
   * in the array. Resolution ("dx") is given by MembershipFunction.RESOLUTION
   */
  public double[] getQualifiedConsequent(FuzzySet fuzzySet, double firingStrength) {
    return membershipFunctions.get(fuzzySet).getQualifiedValues(firingStrength);
  }
  
  /*
   * Returns the domain value starting at which the membership function
   * is sampled (i.e. the x associated with the first y in the lookup table)   
   */
  public double getLowerBound(FuzzySet fuzzySet) {
    return membershipFunctions.get(fuzzySet).getLowerBound();
  }
  

  // (Non-rigorous and incomplete) Testing
  public static void main(String args[]) {
    FuzzySetsManager fs = new FuzzySetsManager();
    System.out.println(fs.getDegreeOfValidity(FuzzySet.V_JUSTRIGHT, 20) + "(1)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.V_JUSTRIGHT, 25) + "(~0.6)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.V_JUSTRIGHT, 5) + "(~0)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.V_JUSTRIGHT, 4.9) + "(0)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.V_JUSTRIGHT, 35) + "(~0)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.V_JUSTRIGHT, 35.1) + "(0)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.A_NONE, 0) + "(1)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.A_NONE, 0.5) + "(~0.6)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.A_NONE, -1) + "(~0.1)");
    
    System.out.println(fs.getLowerBound(FuzzySet.A_ACCELERATE) + "(0.5)");
    System.out.println(fs.getLowerBound(FuzzySet.V_JUSTRIGHT) + "(5)");
    
    fs.updateDesiredParameters(1, 100);
    System.out.println(fs.getDegreeOfValidity(FuzzySet.V_JUSTRIGHT, 100) + "(1)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.D_JUSTRIGHT, 1.25) + "(~0.6)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.D_JUSTRIGHT, 2) + "(0)");
    System.out.println(fs.getLowerBound(FuzzySet.V_JUSTRIGHT) + "(25)");
    
    System.out.println(fs.getDegreeOfValidity(FuzzySet.V_VERYFAST, 150) + "(1)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.V_VERYFAST, 149) + "(~1)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.V_VERYFAST, 200) + "(1)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.V_VERYFAST, 30) + "(0)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.D_VERYCLOSE, 0.5) + "(1)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.D_VERYCLOSE, -1) + "(1)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.D_VERYCLOSE, 0.6) + "(~1)");
    System.out.println(fs.getDegreeOfValidity(FuzzySet.D_VERYCLOSE, 5) + "(0)");
    
    /*System.out.println("(~1/3 values of 0.6):");
    double[] qf = fs.getQualifiedConsequent(FuzzySet.V_JUSTRIGHT, 0.6);
    for (int i = 0; i < qf.length; i++)
      System.out.println(qf[i]);
    */
    fs.updateDesiredParameters(5, 5);
    System.out.println(fs.getDegreeOfValidity(FuzzySet.V_JUSTRIGHT, 5.05));
    System.out.println(fs.getDegreeOfValidity(FuzzySet.V_JUSTRIGHT, 5.00));
    System.out.println(fs.getDegreeOfValidity(FuzzySet.V_JUSTRIGHT, 4.95));
  }
}
